//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <mantle.h>
#include <stub.h>
#include <syscall.h>
#include <clsinfo.h>
#include <kmarshal.h>
#include <kprxstub.h>

#include "rot.h"

EXTERN_C InterfaceID EIID_IProxy;

ECode Proxy_KernToUserMsh_BufferSize(
        /* [in] */ const CIMethodEntry *pMethodInfo,
        /* [in] */ UInt32 *puArgs,
        /* [out] */ UInt32 *puInSize,
        /* [out] */ UInt32 *puOutSize)
{
    int n, cParams;
    UInt32 uInSize, uOutSize, uSize = 0;
    const CIBaseType *pParams;

    uInSize = 0;
    uOutSize = 0;
    cParams = pMethodInfo->paramNum;
    pParams = pMethodInfo->params;

    for (n = 0; n < cParams; n++) {
        if (BT_IS_OUT(pParams[n])) {    // [out]
            uOutSize += sizeof(UInt32); // for pointer

            switch (BT_TYPE(pParams[n])) {
                case BT_TYPE_PUINT8:
                case BT_TYPE_PUINT16:
                case BT_TYPE_PUINT32:
                    uSize = sizeof(UInt32);
                    break;

                case BT_TYPE_PUINT64:
                    uSize = sizeof(UInt64);
                    break;

                case BT_TYPE_PEMUID:
                    uSize = sizeof(EMuid);
                    break;

                case BT_TYPE_PSTRUCT:
                    uSize = BT_TYPE_SIZE(pParams[n]) * 4;
                    break;

                case BT_TYPE_PEGUID:
                    uSize = sizeof(EMuid) + sizeof(wchar_t *) \
                            + 80 * sizeof(WChar);
                    break;

                case BT_TYPE_WSTRINGBUF:
                case BT_TYPE_ASTRINGBUF:
                case BT_TYPE_BUFFEROF:
                case BT_TYPE_ARRAYOF:
                    if (*puArgs) {
                        uSize = sizeof(CarQuintet) + \
                           MSH_ALIGN_4(((PCARQUINTET)(Address*)*puArgs)->m_size);
                    }
                    break;

                case BT_TYPE_PINTERFACE:
                    uSize = sizeof(InterfacePack);
                    break;

                default:
                    MARSHAL_DBGOUT(MSHDBG_ERROR, kprintf(
                        "MshKer2Use: Invalid [in, out] type(%08x).\n",
                        pParams[n]));
                    assert(0);
                    return E_INVALID_ARGUMENT;
            }

            if (*puArgs) {
                uOutSize += uSize;
            }
            // else: null pointer do not contain data.
        }

        if (BT_IS_IN(pParams[n])) { // [in]
            switch (BT_TYPE(pParams[n])) {
                case BT_TYPE_UINT8:
                case BT_TYPE_UINT16:
                case BT_TYPE_UINT32:
                    uInSize += sizeof(UInt32);
                    puArgs++;
                    break;

                case BT_TYPE_PUINT8:
                case BT_TYPE_PUINT16:
                case BT_TYPE_PUINT32:
                    if (*puArgs) {
                        uInSize += sizeof(UInt32) * 2;
                    }
                    else { // null pointer
                        uInSize += sizeof(UInt32);
                    }
                    puArgs++;
                    break;

                case BT_TYPE_UINT64:
#ifdef _mips
                    // Adjust for 64bits align on mips
                    if (!(n % 2)) {
                        uInSize += 4;
                        puArgs += 1;
                    }
#endif
                    uInSize += sizeof(UInt64);
                    puArgs += 2;
                    break;

                case BT_TYPE_PUINT64:
                    if (*puArgs) {
                        uInSize += sizeof(UInt32) + sizeof(UInt64);
                    }
                    else { // null pointer
                        uInSize += sizeof(UInt32);
                    }
                    puArgs++;
                    break;

                case BT_TYPE_EGUID:
                    uInSize += sizeof(EMuid) + sizeof(wchar_t *) \
                              + 80 * sizeof(wchar_t);
                    puArgs += sizeof(EMuid) / 4 + sizeof(wchar_t *) / 4;
                    break;

                case BT_TYPE_EMUID:
                    uInSize += sizeof(EMuid);
                    puArgs += sizeof(EMuid) / 4;
                    break;

                case BT_TYPE_PEGUID:
                    if (*puArgs) {
                        uInSize += sizeof(UInt32) + sizeof(EMuid)
                                   + 80 * sizeof(wchar_t)
                                   + sizeof(wchar_t *);
                    }
                    else {  // null pointer
                        uInSize += sizeof(UInt32);
                    }
                    puArgs++;
                    break;

                case BT_TYPE_PEMUID:
                    if (*puArgs) {
                        uInSize += sizeof(UInt32) + sizeof(EMuid);
                    }
                    else {  // null pointer
                        uInSize += sizeof(UInt32);
                    }
                    puArgs++;
                    break;
                case BT_TYPE_STRUCT:
                case BT_TYPE_PSTRUCT:
                    if (*puArgs) {
                        uInSize += sizeof(UInt32) + BT_TYPE_SIZE(pParams[n]) * 4;
                    }
                    else {  // null pointer
                        uInSize += sizeof(UInt32);
                    }
                    puArgs++;
                    break;

                case BT_TYPE_ASTRING:
                    if (*puArgs) {
                        UInt32 nBufLen = (strlen((char *)(*puArgs)) + 1)
                            * sizeof(char);
                        uInSize += sizeof(UInt32) + sizeof(UInt32) +
                            MSH_ALIGN_4(nBufLen);
                    }
                    else {
                        uInSize += sizeof(UInt32);
                    }
                    puArgs++;
                    break;

                case BT_TYPE_WSTRING:
                    if (*puArgs) {
                        UInt32 nBufLen = (wcslen((wchar_t *)(*puArgs)) + 1)
                            * sizeof(wchar_t);
                        uInSize += sizeof(UInt32) + sizeof(UInt32) +
                            MSH_ALIGN_4(nBufLen);
                    }
                    else {
                        uInSize += sizeof(UInt32);
                    }
                    puArgs++;
                    break;

                case BT_TYPE_INTERFACE:
                    if (*puArgs) {
                        uInSize += sizeof(InterfacePack);
                    }
                    else {  // null pointer
                        uInSize += sizeof(UInt32);
                    }
                    puArgs++;
                    break;

                case BT_TYPE_PINTERFACE:
                    if (*puArgs) {
                        if (*(UInt32 *)(*puArgs)) {
                            uInSize += sizeof(InterfacePack);
                        }
                        else {
                            uInSize += sizeof(UInt32);
                        }
                    }
                    uInSize += sizeof(UInt32);  // for pointer
                    puArgs++;
                    break;

                case BT_TYPE_WSTRINGBUF:
                case BT_TYPE_ASTRINGBUF:
                case BT_TYPE_BUFFEROF:
                case BT_TYPE_ARRAYOF:
                    if (*puArgs) {
                        uInSize += MSH_ALIGN_4(sizeof(UInt32) \
                          + sizeof(CarQuintet) \
                          + ((PCARQUINTET)(Address*)*puArgs)->m_size);
                    }
                    else {  // null pointer
                        uInSize += sizeof(UInt32);
                    }
                    puArgs++;
                    break;

                default:
                    MARSHAL_DBGOUT(MSHDBG_ERROR, kprintf(
                        "MshKer2Use: Invalid [in, out] type(%08x).\n",
                        pParams[n]));
                    assert(0);
                    return E_INVALID_ARGUMENT;
            }
        }
        else {  // [out]
            if (((BT_TYPE(pParams[n]) == BT_TYPE_BUFFEROF) ||
                (BT_TYPE(pParams[n]) == BT_TYPE_ARRAYOF) ||
                (BT_TYPE(pParams[n]) == BT_TYPE_WSTRINGBUF) ||
                (BT_TYPE(pParams[n]) == BT_TYPE_ASTRINGBUF)) && *puArgs) {
                uInSize += sizeof(UInt32); // for size only
            }
            puArgs++;
            uInSize += sizeof(UInt32);
        }
    }

    *puInSize = uInSize;
    *puOutSize = uOutSize;
    return NOERROR;
}

ECode Proxy_KernToUserMsh_In(
        /* [in] */ const CIMethodEntry *pMethodInfo,
        /* [in] */ UInt32 *puArgs,
        /* [in, out] */ UInt32 *puBuffer)
{
    int n, cParams;
    const CIBaseType *pParams;
    ECode ec;

    cParams = pMethodInfo->paramNum;
    pParams = pMethodInfo->params;

    for (n = 0; n < cParams; n++) {
        if (BT_IS_IN(pParams[n])) { // [in] or [in, out]
            switch (BT_TYPE(pParams[n])) {
                case BT_TYPE_UINT8:
                case BT_TYPE_UINT16:
                case BT_TYPE_UINT32:
                    *puBuffer = *puArgs;
                    puArgs++;
                    puBuffer++;
                    break;

                case BT_TYPE_PUINT8:
                case BT_TYPE_PUINT16:
                case BT_TYPE_PUINT32:
                    if (*puArgs) {
                        *puBuffer = MSH_NOT_NULL;
                        puBuffer++;
                        *puBuffer = *(UInt32 *)(*puArgs);
                    }
                    else {  // null pointer
                        *puBuffer = MSH_NULL;
                    }
                    puBuffer++;
                    puArgs++;
                    break;

                case BT_TYPE_UINT64:
#ifdef _mips
                    // Adjust for 64bits align on mips
                    if (!(n % 2)) {
                        puArgs += 1;
                        puBuffer += 1;
                    }
#endif
                    *(UInt64 *)puBuffer = *(UInt64 *)puArgs;
                    puArgs += 2;
                    puBuffer += 2;
                    break;

                case BT_TYPE_PUINT64:
                    if (*puArgs) {
                        *puBuffer = MSH_NOT_NULL;
                        puBuffer++;
                        *(UInt64 *)puBuffer = *(UInt64 *)(*puArgs);
                        puBuffer += 2;
                    }
                    else {  // null pointer
                        *puBuffer = MSH_NULL;
                        puBuffer++;
                    }
                    puArgs++;
                    break;

                case BT_TYPE_EGUID:
                    *(EMuid *)puBuffer = *(EMuid *)puArgs;
                    puArgs += sizeof(EMuid) / 4;
                    puBuffer += sizeof(EMuid) / 4;
                    *puBuffer = MSH_NOT_NULL;
                    puBuffer++;
                    memcpy(puBuffer, (void*)*puArgs, 160);
                    puArgs++;
                    puBuffer += 40;
                    break;

                case BT_TYPE_EMUID:
                    *(EMuid *)puBuffer = *(EMuid *)puArgs;
                    puArgs += sizeof(EMuid) / 4;
                    puBuffer += sizeof(EMuid) / 4;
                    break;

                case BT_TYPE_PEGUID:
                    if (*puArgs) {
                        *puBuffer = MSH_NOT_NULL;
                        puBuffer++;
                        *(EMuid *)puBuffer = *(EMuid *)(*puArgs);
                        puBuffer += sizeof(EMuid) / 4;
                        *puBuffer = MSH_NOT_NULL;
                        puBuffer++;
                        memcpy(puBuffer, ((ClassID *)*puArgs)->pUunm, 160);
                        puBuffer += 40;

                    }
                    else {  // null pointer
                        *puBuffer = MSH_NULL;
                        puBuffer++;
                    }
                    puArgs++;
                    break;

                case BT_TYPE_PEMUID:
                    if (*puArgs) {
                        *puBuffer = MSH_NOT_NULL;
                        puBuffer++;
                        *(EMuid *)puBuffer = *(EMuid *)(*puArgs);
                        puBuffer += sizeof(EMuid) / 4;
                    }
                    else {  // null pointer
                        *puBuffer = MSH_NULL;
                        puBuffer++;
                    }
                    puArgs++;
                    break;

                case BT_TYPE_STRUCT:
                case BT_TYPE_PSTRUCT:
                    if (*puArgs) {
                        *puBuffer = MSH_NOT_NULL;
                        puBuffer++;
                        memcpy((PByte)puBuffer, (PByte)(*puArgs), BT_TYPE_SIZE(pParams[n]) * 4);
                        puBuffer += BT_TYPE_SIZE(pParams[n]);
                    }
                    else {  // null pointer
                        *puBuffer = MSH_NULL;
                        puBuffer++;
                    }
                    puArgs++;
                    break;

                case BT_TYPE_WSTRINGBUF:
                case BT_TYPE_ASTRINGBUF:
                case BT_TYPE_BUFFEROF:
                case BT_TYPE_ARRAYOF:
                    if (*puArgs) {
                        *puBuffer = MSH_NOT_NULL;
                        puBuffer++;
                        memcpy(puBuffer, ((Byte *)*puArgs), \
                                sizeof(CarQuintet)); // copy head
                        puBuffer = (UInt32 *)((Byte *)puBuffer
                                    + sizeof(CarQuintet));
                        // copy the storaged data
                        //
                        memcpy(puBuffer, ((PCARQUINTET)(Address*)*puArgs)->m_pBuf,
                            ((PCARQUINTET)(Address*)*puArgs)->m_size);
                        puBuffer = (UInt32 *)((Byte *)puBuffer + \
                            MSH_ALIGN_4(((PCARQUINTET)(Address*)*puArgs)-> \
                                m_size));
                    }
                    else {  // null pointer
                        *puBuffer = MSH_NULL;
                        puBuffer++;
                    }
                    puArgs++;
                    break;

                case BT_TYPE_ASTRING:
                    if (*puArgs) {
                        *puBuffer = MSH_NOT_NULL;
                        puBuffer++;

                        UInt32 nBufLen = (strlen((char *)(*puArgs)) + 1)
                            * sizeof(char);
                        *puBuffer = nBufLen;
                        puBuffer++;

                        memcpy(puBuffer, (const void *)(*puArgs), nBufLen);
                        puBuffer = (UInt32 *)
                            ((Byte *)puBuffer + MSH_ALIGN_4(nBufLen));
                    }
                    else {  // null pointer
                        *puBuffer = MSH_NULL;
                        puBuffer++;
                    }
                    puArgs++;
                    break;

                case BT_TYPE_WSTRING:
                    if (*puArgs) {
                        *puBuffer = MSH_NOT_NULL;
                        puBuffer++;

                        UInt32 nBufLen = (wcslen((wchar_t *)(*puArgs)) + 1)
                            * sizeof(wchar_t);
                        *puBuffer = nBufLen;
                        puBuffer++;

                        memcpy(puBuffer, (const void *)(*puArgs), nBufLen);
                        puBuffer = (UInt32 *)
                            ((Byte *)puBuffer + MSH_ALIGN_4(nBufLen));
                    }
                    else {  // null pointer
                        *puBuffer = MSH_NULL;
                        puBuffer++;
                    }
                    puArgs++;
                    break;

                case BT_TYPE_INTERFACE:
                    if (*puArgs) {
                        ec = KernMarshalInterface(
                            (PInterface)*puArgs,
                            (InterfacePack *)puBuffer);
                        if (FAILED(ec)) {
                            MARSHAL_DBGOUT(MSHDBG_ERROR, kprintf(
                                "MshKer2Use: marshal interface, ec = %x\n", ec));
                            return ec;
                        }

                        puBuffer = (UInt32 *)((Byte *)puBuffer
                                                + sizeof(InterfacePack));
                    }
                    else {  // null pointer
                        *puBuffer = 0;
                        puBuffer++;
                    }
                    puArgs++;
                    break;

                case BT_TYPE_PINTERFACE:
                    if (*puArgs) {
                        *puBuffer = MSH_NOT_NULL;
                        puBuffer++;

                        if (*(UInt32 *)(*puArgs)) {
                            ec = KernMarshalInterface(
                                (PInterface)*(UInt32 *)*puArgs,
                                (InterfacePack *)puBuffer);
                            if (FAILED(ec)) {
                                MARSHAL_DBGOUT(MSHDBG_ERROR, kprintf(
                                    "MshKer2Use: marshal interface, ec = %x\n",
                                    ec));
                                return ec;
                            }

                            puBuffer = (UInt32 *)((Byte *)puBuffer
                                                    + sizeof(InterfacePack));
                        }
                        else {
                            *puBuffer = 0;
                            puBuffer++;
                        }
                    }
                    else {
                        *puBuffer = MSH_NULL;
                        puBuffer++;
                    }
                    puArgs++;
                    break;

                default:
                    MARSHAL_DBGOUT(MSHDBG_ERROR, kprintf(
                        "MshKer2Use: Invalid [in, out] type(%08x).\n",
                        pParams[n]));
                    assert(0);
                    return E_INVALID_ARGUMENT;
            }
        }
        else {  // [out]
            *puBuffer = *puArgs ? MSH_NOT_NULL : MSH_NULL;
            puBuffer++;

            if (((BT_TYPE(pParams[n]) == BT_TYPE_BUFFEROF) ||
                (BT_TYPE(pParams[n]) == BT_TYPE_ARRAYOF) ||
                (BT_TYPE(pParams[n]) == BT_TYPE_WSTRINGBUF) ||
                (BT_TYPE(pParams[n]) == BT_TYPE_ASTRINGBUF)) && *puArgs) {
                *puBuffer = ((PCARQUINTET)(Address*)*puArgs)->m_size;
                puBuffer++;
            }
            puArgs++;
        }
    }

    return NOERROR;
}

ECode Proxy_KernToUserUnmsh_Out(
        /* [in] */ const CIMethodEntry *pMethodInfo,
        /* [in] */ UInt32 *puData,
        /* [in] */ UInt32 uDataSize,
        /* [in, out] */ UInt32 *puArgs)
{
    int n, cParams;
    const CIBaseType *pParams;
    ECode ec;

    cParams = pMethodInfo->paramNum;
    pParams = pMethodInfo->params;

    for (n = 0; n < cParams; n++) {
        if (BT_IS_OUT(pParams[n])) {   // [out] or [in, out]
            if (*puArgs) {
                if (*puData != MSH_NOT_NULL) {
                    MARSHAL_DBGOUT(MSHDBG_ERROR, kprintf(
                        "MshKer2Use: arg conflict in proxy's unmarshal.\n"));
                    return E_INVALID_ARGUMENT;
                }
                puData++;

                switch (BT_TYPE(pParams[n])) {
                    case BT_TYPE_PUINT8:
                        *(UInt8 *)(*puArgs) = *(UInt8 *)puData;
                        puData++;
                        break;

                    case BT_TYPE_PUINT16:
                        *(UInt16 *)(*puArgs) = *(UInt16 *)puData;
                        puData++;
                        break;

                    case BT_TYPE_PUINT32:
                        *(UInt32 *)(*puArgs) = *puData;
                        puData++;
                        break;

                    case BT_TYPE_PUINT64:
                        *(UInt64 *)(*puArgs) = *(UInt64 *)puData;
                        puData += 2;
                        break;

                    case BT_TYPE_PEMUID:
                        *(EMuid *)(*puArgs) = *(EMuid *)puData;
                        puData += sizeof(EMuid) / 4;
                        break;

                    case BT_TYPE_PSTRUCT:
                        memcpy((PByte)(*puArgs), (PByte)puData, BT_TYPE_SIZE(pParams[n]) * 4);
                        puData += BT_TYPE_SIZE(pParams[n]);
                        break;

                    case BT_TYPE_PEGUID:
                        *(EMuid *)(*puArgs) = *(EMuid *)puData;
                        puData += sizeof(EMuid) / 4;
                        memcpy((Void*)*(Address*)(*puArgs + sizeof(EMuid)), \
                            puData + 1, 80 * sizeof(WChar));
                        puData += 80 * sizeof(WChar) / 4 + 1;
                        break;

                    case BT_TYPE_WSTRINGBUF:
                    case BT_TYPE_ASTRINGBUF:
                    case BT_TYPE_BUFFEROF:
                    case BT_TYPE_ARRAYOF:
                        memcpy(((PCARQUINTET)(UInt32 *)*puArgs)->m_pBuf, \
                            (Byte *)puData + sizeof(CarQuintet),
                            ((PCARQUINTET)puData)->m_used);

                        ((PCARQUINTET)(UInt32 *)*puArgs)->m_used = \
                                ((PCARQUINTET)puData)->m_used;

                        puData = (UInt32 *)((Byte *)puData + MSH_ALIGN_4(
                                sizeof(CarQuintet)\
                                    + ((PCARQUINTET)(UInt32 *)*puArgs)->m_size));
                        break;

                    case BT_TYPE_PINTERFACE:
                        if (((InterfacePack *)puData)->m_oid != 0) {
                            ec = KernUnmarshalInterface(
                                (InterfacePack *)puData,
                                CTX_KERN_2_USER,
                                (PInterface *)*puArgs);
                            if (FAILED(ec)) {
                                MARSHAL_DBGOUT(MSHDBG_ERROR, kprintf(
                                    "MshKer2Use: unmsh interface, ec = %x\n", ec));
                                return ec;
                            }
                        } else {
                            *((PInterface *)*puArgs) = NULL;

                        }

                        puData += sizeof(InterfacePack) / 4;
                        break;

                    default:
                        MARSHAL_DBGOUT(MSHDBG_ERROR, kprintf(
                            "MshKer2Use: Invalid arg type(%08x)\n", pParams[n]));
                        assert(0);
                        return E_INVALID_ARGUMENT;
                }
            }
            else {
                if (*puData != MSH_NULL) {
                    MARSHAL_DBGOUT(MSHDBG_ERROR, kprintf(
                        "MshKer2Use: arg conflict in proxy's unmarshal.\n"));
                    return E_INVALID_ARGUMENT;
                }
                *puData++;
            }
            puArgs++;
        }
        else {      // [in]
            switch (BT_TYPE(pParams[n])) {
                case BT_TYPE_UINT64:
#ifdef _mips
                    // Adjust for 64bits align on mips
                    if (!(n % 2)) {
                        puArgs += 1;
                    }
#endif
                    puArgs += 2;
                    break;

                case BT_TYPE_EMUID:
                    puArgs += sizeof(EMuid) / 4;
                    break;

                case BT_TYPE_EGUID:
                    puArgs += sizeof(EMuid) / 4 + sizeof(wchar_t *) /4;
                    break;

                default:
                    puArgs++;
                    break;
            }
        }
    }

    return NOERROR;
}

PInterface CInterfaceProxy::S_Probe(
        /* [in] */ CInterfaceProxy *pThis,
        /* [in] */ REIID riid)
{
    return pThis->m_pOwner->Probe(riid);
}

UInt32 CInterfaceProxy::S_AddRef(CInterfaceProxy *pThis)
{
    return pThis->m_pOwner->AddRef();
}

UInt32 CInterfaceProxy::S_Release(CInterfaceProxy *pThis)
{
    return pThis->m_pOwner->Release();
}

ECode CInterfaceProxy::BufferSize(
    /* [in] */ UInt32 uMethodIndex,
    /* [in] */ UInt32 *puArgs,
    /* [out] */ UInt32 *puInSize,
    /* [out] */ UInt32 *puOutSize)
{
    ECode ec;
    const CIMethodEntry *pMethodInfo;
    pMethodInfo = &(m_pInfo->methods[uMethodIndex]);

    ec = Proxy_KernToUserMsh_BufferSize(
         pMethodInfo,
         puArgs,
         puInSize,
         puOutSize);

    if (SUCCEEDED(ec)) {
        *puInSize += sizeof(MarshalHeader);
        if (0 != *puOutSize){
            *puOutSize += sizeof(MarshalHeader);
        }
    }

    return ec;
}

ECode CInterfaceProxy::MarshalIn(
    /* [in] */ UInt32 uMethodIndex,
    /* [in] */ UInt32 *puArgs,
    /* [out] */ MarshalHeader *pHeader)
{
    ECode ec;
    const CIMethodEntry *pMethodInfo;
    pMethodInfo = &(m_pInfo->methods[uMethodIndex]);

    assert(m_pContext == CTX_KERN_2_USER);

    ec = Proxy_KernToUserMsh_In(
         pMethodInfo,
         puArgs,
         (UInt32 *)(pHeader + 1));

    if (SUCCEEDED(ec)) {
        pHeader->m_uMagic = MARSHAL_MAGIC;
        pHeader->m_oid = m_oid;
        pHeader->m_hInterfaceIndex = m_uIndex;
        pHeader->m_hMethodIndex = uMethodIndex + 3;
        pHeader->m_pContext = m_pContext;
    }

    return ec;
}

ECode CInterfaceProxy::UnmarshalOut(
    /* [in] */ UInt32 uMethodIndex,
    /* [out] */ MarshalHeader *pHeader,
    /* [in] */ UInt32 *puArgs)
{
    ECode ec;

    if (pHeader->m_uMagic != MARSHAL_MAGIC) {
        MARSHAL_DBGOUT(MSHDBG_ERROR,
            kprintf("KProxy unmsh: invalid magic(%x)\n", pHeader->m_uMagic));
        return E_MARSHAL_DATA_TRANSPORT_ERROR;
    }

#if defined(_DEBUG)
    if (pHeader->m_oid != m_oid) {
        MARSHAL_DBGOUT(MSHDBG_ERROR,
            kprintf("KProxy unmsh: invalid oid(%x)\n", pHeader->m_oid));
        return E_MARSHAL_DATA_TRANSPORT_ERROR;
    }
    if (pHeader->m_hInterfaceIndex != (Int16)m_uIndex) {
        MARSHAL_DBGOUT(MSHDBG_ERROR,
            kprintf("KProxy unmsh: invalid iidx(%x)\n",
            pHeader->m_hInterfaceIndex));
        return E_MARSHAL_DATA_TRANSPORT_ERROR;
    }
    if (pHeader->m_pContext != m_pContext) {
        MARSHAL_DBGOUT(MSHDBG_ERROR,
        kprintf("Proxy unmsh: invalid ctx(%x)\n", pHeader->m_pContext));
        return E_MARSHAL_DATA_TRANSPORT_ERROR;

    }
    if (pHeader->m_hMethodIndex != (Int16)(uMethodIndex + 3)) {
        MARSHAL_DBGOUT(MSHDBG_ERROR, kprintf(
            "KProxy unmsh: invalid method(%x)\n", pHeader->m_hMethodIndex));
    }
#endif

    assert(m_pContext == CTX_KERN_2_USER);

    ec = Proxy_KernToUserUnmsh_Out(
        &(m_pInfo->methods[uMethodIndex]),
        (UInt32 *)(pHeader + 1),
        pHeader->m_uOutSize - sizeof(MarshalHeader),
        puArgs);

    return ec;
}

#ifdef _arm

EXTERN_C void __ProxyEntry(void);

EXTERN_C ECode GlobalProxyEntry(UInt32 *puArgs)
{
    return CInterfaceProxy::ProxyEntry(puArgs);
}

#if defined(_GNUC)

#define DECL_PROXY_ENTRY()                  \
    __asm__(                                \
        ".text;"                            \
        ".align 4;"                         \
        ".globl "C_PREFIX"__ProxyEntry;"    \
        C_PREFIX"__ProxyEntry:"             \
        "stmdb  sp!, {r1, lr};"             \
        "add    r0, sp, #4;"                \
        "bl     "C_PREFIX"GlobalProxyEntry;"\
        "ldr    lr, [sp, #4];"              \
        "add    sp, sp, #24;"               \
        "mov    pc, lr;"                    \
    )

DECL_PROXY_ENTRY();

#endif // defined(_GNUC)

#endif

#ifdef _mips

EXTERN_C ECode GlobalProxyEntry(UInt32 *puArgs)
{
    return CInterfaceProxy::ProxyEntry(puArgs);
}

#ifdef _GNUC
void __ProxyContinue()
{
DECL_ASMENTRY(ProxyContinue)
#elif defined(_EVC)
DECL_NAKED void ProxyContinue()
{
#else
#error unknown compiler
#endif
    ASM(
        "sw     $4, 0x0($29);"
        "sw     $5, 0x4($29);"
        "sw     $6, 0x8($29);"
        "sw     $7, 0xc($29);"
        "addiu  $29, $29, -0x1c;"
        "sw     $12, 0x10($29);"
        "add    $4, $29, 0x18;"
        "sw     $4, 0x14($29);"
        "la     $9, GlobalProxyEntry;"
        "jalr   $9;"
        "addiu  $29, $29, 0x1c;"
        "lw     $31, -4($29);"
        "jr     $31;");
}
#endif

ECode CInterfaceProxy::ProxyEntry(UInt32 *puArgs)
{
    CInterfaceProxy *pThis;
    UInt32 uMethodIndex, cArgs, uInSize, uOutSize;
    MarshalHeader *pInHeader = NULL, *pOutHeader = NULL;
    ECode ec, orghr;
    int nInUp2Order2 = 0;
    int nOutUp2Order2 = 0;

    puArgs++; // skip ret address

    pThis = (CInterfaceProxy *)*puArgs;
    puArgs++; // skip this

    MARSHAL_DBGOUT(MSHDBG_NORMAL,
        kprintf("Enter KProxy: oid(%x)\n", pThis->m_oid));
    MARSHAL_DBGOUT(MSHDBG_NORMAL, kprintf("iid: "));
    MARSHAL_DBGOUT(MSHDBG_NORMAL, DUMP_GUID(pThis->m_pInfo->iid));

#ifdef _x86
    uMethodIndex = CalcMethodIndex(*(UInt32 *)((UInt32)&puArgs - 4));
#elif defined(_arm)
    uMethodIndex = puArgs[-3];
#elif defined(_mips)
    uMethodIndex = CalcMethodIndex(*(puArgs - 4) - 4);
#else
#error unknown architecture
#endif

    cArgs = pThis->CountMethodArgs(uMethodIndex);

    MARSHAL_DBGOUT(MSHDBG_NORMAL, kprintf(
        "Method index(%d), args size(%d)\n", uMethodIndex + 3, cArgs * 4));

    ec = pThis->BufferSize(uMethodIndex, puArgs, &uInSize, &uOutSize);
    if (FAILED(ec)) {
        MARSHAL_DBGOUT(MSHDBG_ERROR,
            kprintf("KProxy BufferSize(), ec = %x\n", ec));
        goto ProxyExit;
    }
    MARSHAL_DBGOUT(MSHDBG_NORMAL, kprintf(
        "Buffer size: isize(%d), osize(%d)\n", uInSize, uOutSize));
    assert(uInSize >= sizeof(MarshalHeader));

    // NOTE: the thread's kernel stack size is 0x2000
    if (uInSize <= PAGE_SIZE / 3) {
        pInHeader = (MarshalHeader *)_alloca(uInSize);
    }
    else {
        nInUp2Order2 = GET_UP_ORDER(AlignPageUp(uInSize) / PAGE_SIZE);
        pInHeader = (MarshalHeader *)DzAllocKernelPages(nInUp2Order2);
    }
    if (!pInHeader) {
        MARSHAL_DBGOUT(MSHDBG_ERROR, kprintf("kProxy: alloca() failed.\n"));
        ec = E_OUT_OF_MEMORY;
        goto ProxyExit;
    }
    if (uOutSize) {
        if (uOutSize <= PAGE_SIZE / 3) {
            pOutHeader = (MarshalHeader *)_alloca(uOutSize);
        }
        else {

            nOutUp2Order2 = GET_UP_ORDER(AlignPageUp(uOutSize) / PAGE_SIZE);
            pOutHeader = (MarshalHeader *)DzAllocKernelPages(nOutUp2Order2);
        }
        if (!pOutHeader) {
            MARSHAL_DBGOUT(MSHDBG_ERROR, kprintf("kProxy: alloca() failed.\n"));
            ec = E_OUT_OF_MEMORY;
            goto ProxyExit;
        }
    }

    pInHeader->m_uInSize = uInSize;
    pInHeader->m_uOutSize = uOutSize;

    ec = pThis->MarshalIn(uMethodIndex, puArgs, pInHeader);
    if (SUCCEEDED(ec)) {
        MARSHAL_DBGOUT(MSHDBG_NORMAL, kprintf(
            "Before SysInvoke: oid(%d), isize(%d)\n", pThis->m_oid, uInSize));

        InvokeInArg invokeInArg = {pThis->m_oid, FALSE};
        ec = SysInvoke(
            &invokeInArg,
            (void *)pInHeader,
            uInSize,
            (void *)pOutHeader,
            uOutSize);

        MARSHAL_DBGOUT(MSHDBG_NORMAL, kprintf(
            "After SysInvoke: ec(%x), osize(%d)\n",
            ec, pInHeader->m_uOutSize));

        if (pOutHeader && SUCCEEDED(ec)) {
            orghr = pThis->UnmarshalOut(uMethodIndex, pOutHeader, puArgs);
            if (FAILED(orghr)) {
                MARSHAL_DBGOUT(MSHDBG_ERROR,
                    kprintf("KProxy UnmarshalOut failed, ec = %x\n", ec));
                ec = orghr;
            }
        }
        else {
            MARSHAL_DBGOUT(MSHDBG_WARNING,
                kprintf("KProxy SysInvoke() failed. ec = %x\n", ec));
        }
    }
    else {
        MARSHAL_DBGOUT(MSHDBG_ERROR,
            kprintf("KProxy MarshalIn() failed. ec = %x\n", ec));
    }

ProxyExit:
    if (uInSize > PAGE_SIZE / 3 && pInHeader) {
        DzFreeKernelPages(pInHeader, nInUp2Order2);
    }
    if (pOutHeader && uOutSize > PAGE_SIZE / 3) {
        DzFreeKernelPages(pOutHeader, nOutUp2Order2);
    }

    MARSHAL_DBGOUT(MSHDBG_NORMAL, kprintf("Exit KProxy: ec(%x)\n", ec));
    SYS_PROXY_EXIT(ec, &puArgs - 1, cArgs);

// other architecture, "return" in the SYS_PROXY_EXIT
#ifdef _mips
    return ec;
#endif
}

PInterface CObjectProxy::Probe(
    REIID riid)
{
    int n;

    if (riid == EIID_IInterface || riid == EIID_IProxy) {
        return (IInterface *)this;
    }

    for (n = 0; n < m_cInterfaces; n++) {
        if (riid == m_pInterfaces[n].m_pInfo->iid) {
            break;
        }
    }
    if (n == m_cInterfaces) {
        MARSHAL_DBGOUT(MSHDBG_WARNING, kprintf(
            "KProxy(%d): QI failed, iid: ", m_oid));
        MARSHAL_DBGOUT(MSHDBG_WARNING, DUMP_GUID(riid));

        return NULL;
    }

    return (PInterface)&(m_pInterfaces[n].m_pvVptr);
}

UInt32 CObjectProxy::AddRef(void)
{
    Int32 lRefs = m_cRefs.Increment();

    MARSHAL_DBGOUT(MSHDBG_CREF, kprintf(
        "KProxy(%d) AddRef: %d\n", m_oid, lRefs));
    return (UInt32)lRefs;
}

UInt32 CObjectProxy::Release(void)
{
    ECode ec;

    g_marshalLock.Lock();

    Int32 lRefs = m_cRefs.Decrement();

    if (lRefs == 0) {
        MARSHAL_DBGOUT(MSHDBG_NORMAL, kprintf(
            "KProxy(%d) destructed\n", m_oid));

        ec = UnregisterKernImportObject(m_oid);
        if (S_FALSE == ec) {
            g_marshalLock.Unlock();
            return 1; // other thread hold the Stub
        }

        // Release stub's reference before destroy self.
        //
        SysDetachServer(m_oid, m_pInterfaces[0].m_pContext);

        g_marshalLock.Unlock();

        delete this;
        return 0;
    }
    else {
        g_marshalLock.Unlock();
    }

    MARSHAL_DBGOUT(MSHDBG_CREF, kprintf(
        "KProxy(%d) Release: %d\n", m_oid, lRefs));
    return (UInt32)lRefs;
}

ECode CObjectProxy::Aggregate(
        /* [in] */ AggregateType type,
        /* [in] */ PInterface pObj)
{
    return E_NOT_IMPLEMENTED;
}

ECode CObjectProxy::GetDomain(
        /* [out] */ PInterface *ppObj)
{
    return E_NOT_IMPLEMENTED;
}

ECode CObjectProxy::IndexQI(
        /* [in] */ UInt32 uIndex,
        /* [out] */ PInterface *ppObj)
{
    if (uIndex < (UInt32)m_cInterfaces) {
        *ppObj = (IInterface *)&(m_pInterfaces[uIndex].m_pvVptr);
        this->AddRef();
        return NOERROR;
    }
    MARSHAL_DBGOUT(MSHDBG_WARNING, kprintf(
        "KProxy(%d): IndexQI failed - idx(%d), cInterfaces(%d)\n",
        m_oid, uIndex, m_cInterfaces));

    return E_NO_INTERFACE;
}

ECode CObjectProxy::QI_Index(
             /* [in] */ IInterface **ppObj,
             /* [out] */UInt32* pIndex)
{
    Int32 Index = 0;

    while (Index < m_cInterfaces) {
        if ((Void*)*ppObj == &(m_pInterfaces[Index].m_pvVptr)) {
            *pIndex = Index;
            return NOERROR;
        }
        Index++;
    }
    return E_NO_INTERFACE;
}

ECode CObjectProxy::GetOid(
        /* [out] */ oid_t *pOid)
{
    *pOid = m_oid;
    return NOERROR;
}

ECode CObjectProxy::GetClsid(
        /* [out] */ EMuid *pClsid)
{
    *pClsid = ((CIClassEntry*)m_pInfo)->clsid;
    return NOERROR;
}

ECode CObjectProxy::GetClassEntry(
        /* [out] */ UInt32 *pClassEntry)
{
    *pClassEntry = m_pInfo;
    return NOERROR;
}

ECode CObjectProxy::S_CreateObject(
        /* [in] */ oid_t oid,
        /* [in] */ REMuid rclsid,
        /* [in] */ UInt32 * pClassEntry,
        /* [in] */ PContext pContext,
        /* [out] */ IProxy **ppIProxy)
{
    CObjectProxy *pProxy;
    CInterfaceProxy *pInterfaces;
    int n;
    UInt32 Entry;
    ECode ec;

    assert(oid != 0);
    assert(pContext == CTX_KERN_2_USER);
    assert(ppIProxy != NULL);

    pProxy = new CObjectProxy();
    if (!pProxy) {
        MARSHAL_DBGOUT(MSHDBG_ERROR,
            kprintf("Create KProxy object: out of memory.\n"));
        SysDetachServer(oid, pContext);
        return E_OUT_OF_MEMORY;
    }

    ec = LookupServerClassEntry(rclsid, (const CIClassEntry**)&Entry);
    if (SUCCEEDED(ec)) {
        pProxy->m_pInfo = Entry;
    }
    else {
        pProxy->m_pInfo = (UInt32)pClassEntry;
    }
    pProxy->m_oid = oid;
    pProxy->m_cInterfaces = ((CIClassEntry*)(pProxy->m_pInfo))->interfaceNum;
    assert(pProxy->m_cInterfaces);

    pInterfaces = new CInterfaceProxy[pProxy->m_cInterfaces];
    if (!pInterfaces) {
        MARSHAL_DBGOUT(MSHDBG_ERROR,
            kprintf("Create KProxy interfaces: out of memory.\n"));
        ec = E_OUT_OF_MEMORY;
        goto ErrorExit;
    }
    pProxy->m_pInterfaces = pInterfaces;
    memset(pInterfaces, 0, sizeof(CInterfaceProxy) * pProxy->m_cInterfaces);

    for (n = 0; n < pProxy->m_cInterfaces; n++) {
        pInterfaces[n].m_oid = oid;
        pInterfaces[n].m_uIndex = n;
        pInterfaces[n].m_pContext = pContext;
        pInterfaces[n].m_pOwner = pProxy;
        CIInterfaceEntry *pInterfaceInfo =
            (CIInterfaceEntry *)GetUnalignedPtr(
                ((CIClassEntry *)(pProxy->m_pInfo))->interfaces + n);
        pInterfaces[n].m_pInfo = pInterfaceInfo;

        pInterfaces[n].m_pvVptr = g_marshalVtbl;

#ifdef _x86
        pInterfaces[n].m_pvProxyEntry = (PVoid)&CInterfaceProxy::ProxyEntry;
#elif defined(_arm)
        pInterfaces[n].m_pvProxyEntry = (PVoid)&__ProxyEntry;
#elif defined(_mips)
        pInterfaces[n].m_pvProxyEntry = &ProxyContinue;
#else
#error unknown architecture
#endif
    }

    // attach servers here.
    //
    ec = SysAttachServer(oid, pContext);

    ec = RegisterKernImportObject(oid, pProxy);
    if (FAILED(ec)) {
        MARSHAL_DBGOUT(MSHDBG_ERROR, kprintf(
            "Create KProxy: register import object failed, oid(%d), ec(%x)\n",
            oid, ec));
        goto ErrorExit;
    }

    *ppIProxy = (IProxy *)pProxy;
    pProxy->AddRef();
    return NOERROR;

ErrorExit:
    SysDetachServer(oid, pContext);
    delete pProxy;
    return ec;
}

CObjectProxy::~CObjectProxy()
{
    if (m_pInterfaces) {
        delete [] m_pInterfaces;
    }
}
